#pragma once

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cstdint>
#include <vector>
#include <memory>
#include <chrono>
#include "cell.h"
#include "player.h"
#include "pos.h"
#include "game_record.pb.h"

enum error_code {
    MS_OK = 0,          /* 正确 */
    MS_E_PARAM = 1,     /* 参数错误 */
    MS_E_NULLPTR = 2,   /* 空指针 */
    MS_E_MEMORY = 3,    /* 内存错误 */
};

class minesweeper : public std::enable_shared_from_this<minesweeper> {
public:
    minesweeper(std::shared_ptr<player> p1, std::shared_ptr<player> p2, int row_num = 16, int col_num = 30, int bombs_num = 99);

    int init_game(std::vector<pos> &bombs_pos, std::vector<pos> &pos_opened_by_system);
    int row_num() const
    {
        return static_cast<int>(cells_grid_.size());
    }

    int col_num() const
    {
        if (cells_grid_.empty()) {
            return 0;
        } else {
            return static_cast<int>(cells_grid_[0].size());
        }
    }

    int bombs_num_actual()
    {
        int num = 0;
        for (auto& cells : cells_grid_) {
            for (auto& cell : cells) {
                if (cell.is_bomb()) {
                    num++;
                }
            }
        }
        return num;
    }

    void print_bombs()
    {
        for (auto& cells : cells_grid_) {
            for (auto& cell : cells) {
                if (cell.is_bomb()) {
                    printf("* ");
                } else {
                    printf("  ");
                }
            }
            printf("\n");
        }
    }

    void print_bombs_around(const pos& focused_pos) const
    {
        for (int i = 0; i < row_num(); i++) {
            for (int j = 0; j < col_num(); j++) {
                auto& cell = cells_grid_[i][j];
                if (cell.is_bomb()) {
                    if (focused_pos == pos(i, j)) {
                        printf("\033[47;31m* \033[0m");
                    } else {
                        printf("* ");
                    }
                } else {
                    if (focused_pos == pos(i, j)) {
                        printf("\033[47;31m%d \033[0m", cell.bombs_around());
                    } else {
                        printf("%d ", cell.bombs_around());
                    }
                    
                }
            }
            printf("\n");
        }
    }

    void print_cell_state(const pos& focused_pos)
    {
        for (int i = 0; i < row_num(); i++) {
            for (int j = 0; j < col_num(); j++) {
                auto& cell = cells_grid_[i][j];
                if (cell.state() == cell::ORIGINAL) {
                    printf("  ");
                } else if (cell.state() == cell::OPENED_BY_PLAYER1) {
                    printf("1 ");
                } else if (cell.state() == cell::OPENED_BY_PLAYER2) {
                    printf("2 ");
                } else if (cell.state() == cell::OPENED_BY_SYSTEM) {
                    if (focused_pos == pos(i, j)) {
                        printf("\033[47;31mS \033[0m");
                    } else {
                        printf("S ");
                    }
                }
            }
            printf("\n");
        }
    }

    /**
     * @ret: 左键点击后所有被打开的cell
     * @is_game_over: 传出参数，本次鼠标点击事件发生后，游戏是否结束
     */
    std::vector<pos> left_buttom_clicked(std::shared_ptr<player> player, const pos& pos_clicked, bool &is_game_over);
    
    int cell_not_bomb_num() const
    {
        return row_num() * col_num() - bombs_num_;
    }

    /* 判断游戏是否结束 */
    bool is_game_over()
    {
        return cell_not_bomb_opened_num_ >= cell_not_bomb_num();
    }

    std::shared_ptr<player> p1() const
    {
        return p1_;
    }

    std::shared_ptr<player> p2() const
    {
        return p2_;
    }

    bool is_bomb(const pos& pos) const;

private:
    /* bombs_pos: 传出参数，生成的地雷位置 */
    int generate_bombs(const pos& init_pos, std::vector<pos> &bombs_pos);
    bool is_pos_vaild(const pos& pos) const;
    
    cell& cell_at_pos(const pos& pos);
    int set_as_bomb(const pos& pos);
    int cell_num() const { return row_num() * col_num(); }
    /* 生成一个随机位置 */
    int rand_pos(pos& pos, int row_num, int col_num) const;
    int bombs_around(const pos& pos) const;
    void set_all_cells_bombs_around();
    void _left_buttom_clicked(std::shared_ptr<player> player, const pos& pos_clicked,
        std::vector<pos>& opened_pos, bool &is_game_over);
    void caculate_score(std::shared_ptr<player> player, const pos& pos_clicked, const std::vector<pos>& opened_pos);

    std::vector<std::vector<cell>> cells_grid_;
    int bombs_num_;
    int cell_not_bomb_opened_num_;
    std::shared_ptr<player> p1_;
    std::shared_ptr<player> p2_;
    std::chrono::system_clock::time_point start_time_;
    std::chrono::system_clock::time_point end_time_;
    GameRecordProto game_record_;
};
